package server.network;

import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Server{

	private final static int DEFAULT_PORT = 9001;
	private ArrayList<Server_Command> m_liste_clients;
	private ServerSocket m_serveur;
	private String m_racine_path;
	
	private static String COMMAND_PORT = "--port";
	private static String COMMAND_DIR = "--dir";
	private static String COMMAND_HELP = "--help";
	private static String COMMAND_SEPARATOR = "=";
	
	public static String COMMON_USAGE_MSG = 
			"\n I - Utilisation par defaut: \n\n" +
			"* Détails * \n" +
			"\t Lance le serveur dans le répertoire de l'exécutable et sur le port par défaut (" + DEFAULT_PORT + "). \n" +
			"* Commande * \n" +
			"\t> Server \n\n";
	
	public static String OPTIONS_MSG = 
			"\n II - Options: \n\n" +
			COMMAND_PORT +
			"* Détails * \n" +
			"\t Lance le serveur sur le port spécifié. \n" +
			"* Exemple * \n" +
			"\t> Server --port=9001 \n\n" +
			COMMAND_DIR +
			"* Détails * \n" +
			"\t Lance le serveur dans le répertoire spécifié. \n" +
			"* Exemple * \n" +
			"\t> Server --dir=\"/home/my Documents\" \n\n" +
			"Note: les options sont cumulables.";
	
	public static String HELP_MSG = COMMON_USAGE_MSG + OPTIONS_MSG;
	
	public Server()
	{
		launchServer(DEFAULT_PORT, getCurrentDirectory());
	}
	
	public Server(int port)
	{
		launchServer(port, getCurrentDirectory());
	}
	
	public Server(int port, String rootPath)
	{
		launchServer(port, rootPath);
	}
	
	public Server(String rootPath)
	{
		launchServer(DEFAULT_PORT, rootPath);
	}
	
	/**
	 * Obtient le chemin absolu du répertoire où est stocké le programme
	 */
	private String getCurrentDirectory()
	{
		return getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
	}
	
	private void launchServer(int port, String rootPath)
	{
		try{
			m_serveur = new ServerSocket(port);
			m_racine_path = rootPath;
			File racine_serveur = new File(m_racine_path);
			racine_serveur.mkdir();
			m_liste_clients = new ArrayList<Server_Command>();
			System.out.println("Serveur lancé sur le port " + port);
			while(true){
				Socket client = m_serveur.accept();
				m_liste_clients.add(new Server_Command(client, this));
			}
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
		finally
		{
			if (m_serveur != null)
			{
				try {
					m_serveur.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				m_serveur = null;
			}
		}
	}

	public boolean close_server(){
		try {
			m_serveur.close();
			m_liste_clients.clear();
			if(m_serveur.isClosed())
				return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public int get_port(){
		return DEFAULT_PORT;
	}
	
	public ServerSocket get_socket_server(){
		return m_serveur;
	}
	
	public void set_racine_path(String path_name){
		m_racine_path = path_name;
	}
	
	public String get_racine_path(){
		return m_racine_path;
	}
	
	public static void main(String[] args) throws IOException 
	{
		int port = -1;
		String dir = null;
		
		if (args != null)
		{
			if (args.length > 0)
			{
				boolean dirParsed = false;
				boolean portParsed = false;
				
				for (int i=0; i<args.length; i++)
				{
					// Commande chemin du serveur
					if (isCommandDir(args[i]) && !dirParsed)
					{
						String serverPath = getPathFromCommand(args[i]);
						if (serverPath != null)
						{
							try
							{
								File directory = new File(serverPath);
								if (directory.exists() && directory.isDirectory())
								{
									dir = serverPath;
									dirParsed = true;
								}
								else
								{
									invalidArgumentAndExit(args[i]);
								}
							}
							catch(NullPointerException ex1)
							{
								invalidArgumentAndExit(args[i]);
							}
							catch(SecurityException ex2)
							{
								invalidArgumentAndExit(args[i]);
							}
							catch(IOError ex3)
							{
								invalidArgumentAndExit(args[i]);
							}
						}
						else
						{
							invalidArgumentAndExit(args[i]);
						}
					}
					// Commande numéro de port
					else if(isCommandPort(args[i]) && !portParsed)
					{
						Integer IntegerPort = getPortFromCommand(args[i]);
						if (IntegerPort != null)
						{
							port = IntegerPort;
							portParsed = true;
						}
						else
						{
							invalidArgumentAndExit(args[i]);
						}
					}
					// Commande aide
					else if(isCommandHelp(args[i]))
					{
						System.out.println(HELP_MSG);
						System.out.println("\n- Programme terminé -\n");
						System.exit(0);
					}
					// Commande inconnue
					else
					{
						invalidArgumentAndExit(args[i]);
					}
				}
			}
		}
		
		if (port < 0 && dir == null)
		{
			new Server();
		}
		else if(port > -1 && dir != null)
		{
			new Server(port, dir);
		}
		else if(port > -1 && dir == null)
		{
			new Server(port);
		}
		else if(port < 0 && dir != null)
		{
			new Server(dir);
		}
	}
	
	private static void invalidArgumentAndExit(String argument)
	{
		System.out.println(" - Paramètre invalide - \n" + argument + "Tapez " + COMMAND_HELP + " pour obtenir la liste des commandes.\n" + "\n - Programme terminé -");
		System.exit(0);
	}
	
	private static boolean isCommandPort(String command)
	{
		if (command != null)
			return command.startsWith(COMMAND_PORT) && hasCommandSeparator(command);
		return false;
	}
	
	private static boolean isCommandDir(String command)
	{
		if (command != null)
			return command.startsWith(COMMAND_DIR) && hasCommandSeparator(command);
		return false;
	}
	
	private static boolean isCommandHelp(String command)
	{
		if (command != null)
			return command.startsWith(COMMAND_HELP);
		return false;
	}
	
	private static boolean hasCommandSeparator(String command)
	{
		if (command != null)
			return command.contains(COMMAND_SEPARATOR);
		return false;
	}
	
	/**
	 * Obtient le port depuis une commande passée en paramètre.
	 * @param command
	 * La commande à partir de laquelle il faut extraire le port.
	 * @return
	 * Le numéro de port si la commande est correcte, sinon null.
	 */
	private static Integer getPortFromCommand(String command)
	{
		if (command != null)
		{
			int portBeginIndex = command.indexOf(COMMAND_SEPARATOR) + COMMAND_SEPARATOR.length();
			
			if (portBeginIndex >= 0 && portBeginIndex < command.length()-1)
			{
				String sPort = command.substring(portBeginIndex);
				try
				{
					Integer iPort = new Integer(Integer.parseInt(sPort));
					return iPort;
				}
				catch(NumberFormatException ex)
				{
					return null;
				}
			}
		}
		return null;
	}
	
	/**
	 * Obtient le chemin depuis une commande passée en paramètre.
	 * @param command
	 * La commande à partir de laquelle il faut extraire le chemin.
	 * @return
	 * Retourne le chemin si la commande est correcte, retourne null sinon.
	 */
	private static String getPathFromCommand(String command)
	{
		if (command != null)
		{
			int pathBeginIndex = command.indexOf(COMMAND_SEPARATOR) + COMMAND_SEPARATOR.length();
			
			if (pathBeginIndex >= 0 && pathBeginIndex < command.length()-1)
			{
				String sPath = command.substring(pathBeginIndex);
				
				// Suppression du guillemet de départ
				if (sPath.startsWith("\"") || sPath.startsWith("\'"))
				{
					if (sPath.length() > 1)
					{
						sPath.substring(1);
					}
					else
					{
						return null;
					}
				}
				
				// Suppression du guillemet de fin
				if (sPath.endsWith("\"") || sPath.endsWith("\'"))
				{
					if (sPath.length() > 1)
					{
						sPath.substring(0, sPath.length()-2);
					}
					else
					{
						return null;
					}
				}
				
				// Suppression des éventuels espaces de part et d'autre du chemin
				sPath = sPath.trim();
				
				return sPath;
			}
		}
		return null;
	}
}